#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include "pw_pbl/DigitEditStr.hpp"

using namespace pw_pbl;

struct SStrTransTest
{
	const char* pSrc;
	std::size_t SrcCnt;
	const char* pTgt;
	std::size_t TgtCnt;

	template<std::size_t NS, std::size_t NT>
	constexpr SStrTransTest(const char (&src)[NS], const char (&tgt)[NT]) :
		pSrc{src}, SrcCnt{NS - 1}, pTgt{tgt}, TgtCnt{NT - 1} {static_assert(NS != 0 && NT != 0);}
};


TEST_CASE("DigitEditStr"){
	SUBCASE("CountBit"){
		SUBCASE("base:2"){
			SUBCASE("uint8_t"){
				const uint8_t ta[] = {0, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0b10000000};
				for(auto i = 0; i <= 8; i++)
					CHECK(IDigitEditStr::CountBit(ta[i], 2) == i);
				uint8_t a = std::numeric_limits<uint8_t>::max();
				CHECK(IDigitEditStr::CountBit(a, 2) == 8);
			}
			SUBCASE("int8_t"){
				const int8_t ta[] = {0, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000};
				for(auto i = 0; i <= 7; i++)
				{
					CHECK(IDigitEditStr::CountBit(ta[i], 2) == i);
					if(i != 0)
						CHECK(IDigitEditStr::CountBit(-ta[i], 2) == i + 1);
					else
						CHECK(IDigitEditStr::CountBit(-ta[i], 2) == 0);
				}
				int8_t a = std::numeric_limits<int8_t>::max();
				CHECK(IDigitEditStr::CountBit(a, 2) == 7);
				a = std::numeric_limits<int8_t>::min();
				CHECK(IDigitEditStr::CountBit(a, 2) == 9);
			}
			SUBCASE("unsigned"){
				unsigned a = std::numeric_limits<unsigned>::max();
				CHECK(IDigitEditStr::CountBit(a, 2) == sizeof(unsigned) * CHAR_BIT);
				a = 0;
				CHECK(IDigitEditStr::CountBit(a, 2) == 0);
				a = 0b10011011;
				CHECK(IDigitEditStr::CountBit(a, 2) == 8);

			}
			SUBCASE("int"){
				int a = std::numeric_limits<int>::max();
				CHECK(IDigitEditStr::CountBit(a, 2) == sizeof(int) * CHAR_BIT - 1);
				a = std::numeric_limits<int>::min();
				CHECK(IDigitEditStr::CountBit(a, 2) == sizeof(int) * CHAR_BIT + 1);
				a = 0b10111101;
				CHECK(IDigitEditStr::CountBit(a, 2) == 8);
			}
			SUBCASE("uintmax_t"){
				std::uintmax_t a = std::numeric_limits<std::uintmax_t>::max();
				CHECK(IDigitEditStr::CountBit(a, 2) == sizeof(std::uintmax_t) * CHAR_BIT);
				a = 0;
				CHECK(IDigitEditStr::CountBit(a, 2) == 0);
				a = 0b10011011;
				CHECK(IDigitEditStr::CountBit(a, 2) == 8);

			}
			SUBCASE("intmax_t"){
				std::intmax_t a = std::numeric_limits<std::intmax_t>::max();
				CHECK(IDigitEditStr::CountBit(a, 2) == sizeof(std::intmax_t) * CHAR_BIT - 1);
				a = std::numeric_limits<std::intmax_t>::min();
				CHECK(IDigitEditStr::CountBit(a, 2) == sizeof(std::intmax_t) * CHAR_BIT + 1);
				a = 0b10111101;
				CHECK(IDigitEditStr::CountBit(a, 2) == 8);
			}
		}
	}
	SUBCASE("ToChars"){
		char buf[] = "0123456789ABCDEF";
		REQUIRE(IDigitEditStr::ToChars(0, 10, 0, buf, 16) == 1);
		CHECK_MESSAGE(std::strncmp(buf, "0", 1) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(1, 10, 0, buf, 16) == 1);
		CHECK_MESSAGE(std::strncmp(buf, "1", 1) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(-1, 10, 0, buf, 16) == 2);
		CHECK_MESSAGE(std::strncmp(buf, "-1", 2) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(12, 10, 0, buf, 16) == 2);
		CHECK_MESSAGE(std::strncmp(buf, "12", 2) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(-12, 10, 0, buf, 16) == 3);
		CHECK_MESSAGE(std::strncmp(buf, "-12", 3) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(312, 10, 0, buf, 16) == 3);
		CHECK_MESSAGE(std::strncmp(buf, "312", 3) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(-312, 10, 0, buf, 16) == 4);
		CHECK_MESSAGE(std::strncmp(buf, "-312", 4) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(4312, 10, 0, buf, 4) == 4);
		CHECK_MESSAGE(std::strncmp(buf, "4312", 4) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(4312, 10, 0, buf, 3) < 0);
		REQUIRE(IDigitEditStr::ToChars(-4312, 10, 0, buf, 5) == 5);
		CHECK_MESSAGE(std::strncmp(buf, "-4312", 5) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(-4312, 10, 0, buf, 4) < 0);

		REQUIRE(IDigitEditStr::ToChars(0, 10, 2, buf, 16) == 1);
		CHECK_MESSAGE(std::strncmp(buf, "0", 1) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(1, 10, 2, buf, 16) == 4);
		CHECK_MESSAGE(std::strncmp(buf, "0.01", 4) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(-1, 10, 2, buf, 16) == 5);
		CHECK_MESSAGE(std::strncmp(buf, "-0.01", 5) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(10, 10, 2, buf, 16) == 4);
		CHECK_MESSAGE(std::strncmp(buf, "0.10", 4) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(-10, 10, 2, buf, 16) == 5);
		CHECK_MESSAGE(std::strncmp(buf, "-0.10", 5) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(102, 10, 2, buf, 16) == 4);
		CHECK_MESSAGE(std::strncmp(buf, "1.02", 4) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(-102, 10, 2, buf, 16) == 5);
		CHECK_MESSAGE(std::strncmp(buf, "-1.02", 5) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(3102, 10, 2, buf, 16) == 5);
		CHECK_MESSAGE(std::strncmp(buf, "31.02", 5) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(-3102, 10, 2, buf, 16) == 6);
		CHECK_MESSAGE(std::strncmp(buf, "-31.02", 6) == 0, buf);

		REQUIRE(IDigitEditStr::ToChars(3, 10, 2, buf, 4) == 4);
		CHECK_MESSAGE(std::strncmp(buf, "0.03", 4) == 0, buf);
		REQUIRE(IDigitEditStr::ToChars(3, 10, 3, buf, 4) < 0);
	}
	SUBCASE("RemovePrefixZero"){
		static constexpr SStrTransTest testData[] = {
			{"0", "0"},
			{"00", "0"},
			{"000", "0"},
			{"0.", "0."},
			{"0.0", "0.0"},
			{"00.0", "0.0"},
			{"000.0", "0.0"},
			{"1", "1"},
			{"01", "1"},
			{"001", "1"},
			{"0012", "12"},
		};
		for(auto data : testData)
		{
			SUBCASE(data.pSrc){
				char buf[data.SrcCnt];
				std::memcpy(buf, data.pSrc, data.SrcCnt);
				CHECK(IDigitEditStr::RemovePrefixZero(buf, 0) == 0);
				REQUIRE(IDigitEditStr::RemovePrefixZero(buf, uint8_t(data.SrcCnt)) == data.TgtCnt);
				REQUIRE(std::strncmp(buf, data.pTgt, data.TgtCnt) == 0);
			}
		}
	}
	SUBCASE("construct"){
		TDigitEditStr<0> a;
		REQUIRE(sizeof(a) == sizeof(IDigitEditStr) + 1);
	}
}
